import logging
import time
import zoneinfo
from datetime import datetime

from django.conf import settings
from django.utils.translation import gettext_lazy, gettext as _
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.serializers import Serializer, ValidationError
from rest_framework.decorators import action
from drf_yasg.utils import swagger_auto_schema, no_body
from drf_yasg import openapi

from ceph.models import GlobalConfig
from logrecord.manager import user_operation_record
from utils.permissions import APIIPPermission
from utils.view import CustomGenericViewSet
from api import permissions
from api import serializers
from api import exceptions
from api.paginations import ListBucketCursorPagination
from api.validators import DNSStringValidator
from api.views_v1.views import serializer_error_text
from buckets.utils import create_bucket
from buckets.models import get_next_bucket_max_id, Bucket
from buckets.handlers.nextcloud_db import NextcloudHandler, NextcloudDBUserError
from users.models import UserProfile

logger = logging.getLogger('django.request')    # 这里的日志记录器要和setting中的loggers选项对应，不能随意给参


class AdminBucketViewSet(CustomGenericViewSet):
    """
    管理员
    """
    pagination_class = ListBucketCursorPagination
    permission_classes = [APIIPPermission, IsAuthenticated]
    lookup_field = 'bucket_name'

    @swagger_auto_schema(
        operation_summary=gettext_lazy('超级管理员/资源管理员列举存储桶'),
        responses={
            200: ""
        }
    )
    def list(self, request, *args, **kwargs):
        """
        管理员列举存储桶

            http code 200:
            {
              "next": "http://159.226.235.188/api/v1/admin/bucket?cursor=cD0x&size=1",
              "previous": null,
              "results": [
                {
                  "id": 1,
                  "name": "test",
                  "user": {
                    "id": 1,
                    "username": "shun"
                  },
                  "created_time": "2022-08-03T15:49:13.380915+08:00",
                  "access_permission": "私有",
                  "ftp_enable": false,
                  "access_code": 2,     # 1: 公有（可读）；2： 私有； 3：公有（可读写）
                  "remarks": ""
                }
              ]
            }
        """

        if not request.user.is_superuser:
            try:
                self.check_resource_admin(request=request)
            except exceptions.NotFound as exc:
                return Response(data=exc.err_data(), status=exc.status_code)

        queryset = Bucket.objects.select_related('user').all()
        page = self.paginate_queryset(queryset)
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)

    @swagger_auto_schema(
        operation_summary=gettext_lazy('超级管理员/资源管理员为指定用户创建存储桶'),
        responses={
            200: ""
        }
    )
    def create(self, request, *args, **kwargs):
        """
        超级管理员/资源管理员为指定用户创建存储桶

            http code 200 ok:
            {
              "id": 2,
              "name": "test2",
              "user": {
                "id": 2,
                "username": "sd"
              },
              "created_time": "2022-08-26T16:45:42.469555+08:00",
              "access_permission": "私有",
              "ftp_enable": false,
              "ftp_password": "28403e038c",
              "ftp_ro_password": "d14e2442ef",
              "remarks": "",
              "size": 0,   # 存储桶大小， 不定时统计
              "upper_limit": 0,  # 存储桶上限大小
              "expiration_time": null   # 存储桶过期时间
            }

            http code 400, 403, 409, 500 error:
            {
              "code": "BucketAlreadyExists",
              "message": "存储桶已存在，请更换另一个存储桶名程后再重试。"
            }

            * code:
            400：
                BadRequest: 请求格式错误
                InvalidBucketName: 无效的存储桶名
                InvalidUsername: 无效的用户名
            403：
                AccessDenied： 您没有执行该操作的权限。
            409：
                BucketAlreadyExists：存储桶已存在，请更换另一个存储桶名程后再重试。
            500：
                InternalError：创建存储桶时错误 / 创建用户错误 / 内部错误
        """
        resource_admin_bool = False

        if not request.user.is_superuser:
            try:
                resource_admin_bool = self.check_resource_admin(request=request)
            except exceptions.NotFound as exc:
                return Response(data=exc.err_data(), status=exc.status_code)

        serializer = self.get_serializer(data=request.data)
        if not serializer.is_valid(raise_exception=False):
            s_errors = serializer.errors
            if 'name' in s_errors:
                exc = exceptions.BadRequest(message=_('无效的存储桶名。') + s_errors['name'][0],
                                            code='InvalidBucketName')
            elif 'username' in s_errors:
                exc = exceptions.BadRequest(message=_('无效的用户名。') + s_errors['username'][0],
                                            code='InvalidUsername')
            else:
                code_text = serializer_error_text(serializer.errors, default='参数验证有误')
                exc = exceptions.BadRequest(message=_(code_text))

            return Response(data=exc.err_data(), status=exc.status_code)

        data = serializer.validated_data
        bucket_name = data['name']
        username = data['username']
        upper_limit = data['upper_limit']
        expiration_time = float(data['expiration_timestamp'])
        if not username:
            exc = exceptions.BadRequest(message=_('用户无效。'), code='InvalidUsername')
            return Response(data=exc.err_data(), status=exc.status_code)

        time_ = time.time()
        if expiration_time != 0 and expiration_time <= time_:
            exc = exceptions.BadRequest(message=_('时间戳小于当前时间'))
            return Response(data=exc.err_data(), status=exc.status_code)
        elif expiration_time != 0 and expiration_time > time_:
            expiration_time = datetime.fromtimestamp(float(expiration_time), tz=zoneinfo.ZoneInfo(settings.TIME_ZONE))
        else:
            expiration_time = None

        try:
            bucket_name = self.validate_bucket_name(bucket_name=bucket_name)
        except exceptions.Error as exc:
            return Response(data=exc.err_data(), status=exc.status_code)

        # 检测桶是否存在
        bucket = Bucket.get_bucket_by_name(bucket_name)
        if bucket:
            exc = exceptions.BucketAlreadyExists(message=_('存储桶已存在，请更换另一个存储桶名程后再重试。'))
            return Response(data=exc.err_data(), status=exc.status_code)

        user = UserProfile.objects.filter(username=username).first()
        if user is None:
            try:
                user = UserProfile(username=username)
                user.save(force_insert=True)
            except Exception as e:
                exc = exceptions.Error(message=_('创建用户错误。') + str(e))
                return Response(data=exc.err_data(), status=exc.status_code)

        bucket_id = get_next_bucket_max_id()
        try:
            bucket = create_bucket(_id=bucket_id, name=bucket_name, user=user, upper_limit=upper_limit,
                                   expiration_time=expiration_time)
        except Exception as e:
            exc = exceptions.Error(message=_('创建存储桶时错误。') + str(e))
            return Response(data=exc.err_data(), status=exc.status_code)

        user_operation_record.add_log(request=request,
                                      operation_content=f'创建一个存储桶, 桶名称:{bucket.name}', real_user=username)

        if resource_admin_bool:
            return Response(data=serializers.ResourceAdminBucketSerializer(instance=bucket).data, status=200)

        return Response(data=serializers.BucketSerializer(instance=bucket).data, status=200)

    @staticmethod
    def validate_bucket_name(bucket_name: str):
        """
        :return:
            bucket_name: str

        :raises: ValidationError
        """
        if not bucket_name:
            raise exceptions.BadRequest(message=_('存储桶名称不能为空'), code='InvalidBucketName')

        if bucket_name.startswith('-') or bucket_name.endswith('-'):
            raise exceptions.BadRequest(message=_('存储桶名称不能以“-”开头或结尾'), code='InvalidBucketName')

        try:
            DNSStringValidator(bucket_name)
        except ValidationError:
            raise exceptions.BadRequest(message=_('存储桶名称只能包含小写英文字母、数字和连接线“-”'),
                                        code='InvalidBucketName')

        return bucket_name.lower()

    @swagger_auto_schema(
        operation_summary=gettext_lazy('超级管理员/资源管理员删除指定用户的存储桶'),
        responses={
            200: ""
        }
    )
    @action(methods=['delete'], detail=True, url_path=r'user/(?P<username>[^/]+)', url_name='delete-bucket')
    def delete_bucket(self, request, *args, **kwargs):
        """
        超级管理员/资源管理员删除指定用户的存储桶； 需要超级用户权限或APP超级用户权限， 如果nextcloud 服务中存在该桶的信息也同样删除

            http code 204 ok:
            {}

            http code 400, 403, 409, 500 error:
            {
              "code": "NoSuchBucket",
              "message": "存储桶不存在。"
            }

            * code:
            403：
                AccessDenied： 您没有执行该操作的权限。
            404：
                NoSuchBucket：存储桶不存在
            409：
                BucketNotOwnedUser：存储桶不属于指定用户。
            500：
                InternalError：删除存储桶时错误
        """
        bucket_name = kwargs.get(self.lookup_field)
        username = kwargs.get('username')

        user_operation_record.add_log(request=request,
                                      operation_content=f'删除一个存储桶, 桶名称:{bucket_name}', real_user=username)

        if not request.user.is_superuser:
            # 资源管理员
            try:
                self.check_resource_admin(request=request)
            except exceptions.NotFound as exc:
                return Response(data=exc.err_data(), status=exc.status_code)

        bucket = Bucket.get_bucket_by_name(bucket_name)
        if not bucket:
            exc = exceptions.NoSuchBucket(message=_('存储桶不存在。'))
            return Response(data=exc.err_data(), status=exc.status_code)

        if bucket.user.username != username:
            exc = exceptions.ConflictError(message=_('存储桶不属于指定用户。'), code='BucketNotOwnedUser')
            return Response(data=exc.err_data(), status=exc.status_code)

        try:
            self.delete_nextcloud(bucket=bucket)
        except Exception as e:
            pass

        if not bucket.delete_and_archive():  # 删除归档
            exc = exceptions.Error(message=_('删除存储桶失败'))
            return Response(data=exc.err_data(), status=exc.status_code)

        return Response(status=204)

    @swagger_auto_schema(
        operation_summary=gettext_lazy('超级管理员/资源管理员给存储桶加锁或解锁'),
        request_body=no_body,
        manual_parameters=[
            openapi.Parameter(
                name='bucket_name',
                in_=openapi.IN_PATH,
                type=openapi.TYPE_STRING,
                required=True,
                description='存储桶名称'
            ),
            openapi.Parameter(
                name='lock',
                in_=openapi.IN_PATH,
                type=openapi.TYPE_STRING,
                required=True,
                description='lock-free: 无锁（读写正常）; lock-write: 锁定写（不允许上传删除）; '
                            'lock-readwrite: 锁定读写（不允许上传下载删除）'
            ),
        ],
        responses={
            200: ""
        }
    )
    @action(methods=['post'], detail=True, url_path=r'lock/(?P<lock>[^/]+)', url_name='lock-bucket')
    def lock_bucket(self, request, *args, **kwargs):
        """
        超级管理员/资源管理员给存储桶加锁或解锁； 需要超级用户权限或APP超级用户权限

            http code 200 ok:
            {}

            http code 400, 403, 404, 500 error:
            {
              "code": "NoSuchBucket",
              "message": "存储桶不存在。"
            }

            * code:
            400:
                InvalidLock: 参数“lock”的值无效，锁选项无效。
            403：
                AccessDenied： 您没有执行该操作的权限。
            404：
                NoSuchBucket：存储桶不存在
            500：
                InternalError：存储桶加锁错误
        """
        bucket_name = kwargs.get(self.lookup_field)
        lock = kwargs.get('lock')

        if lock not in ['lock-free', 'lock-write', 'lock-readwrite']:
            exc = exceptions.InvalidArgument(message=_('参数“lock”的值无效，锁选项无效。'), code='InvalidLock')
            return Response(data=exc.err_data(), status=exc.status_code)

        bucket = Bucket.get_bucket_by_name(bucket_name)
        if not bucket:
            exc = exceptions.NoSuchBucket(message=_('存储桶不存在。'))
            return Response(data=exc.err_data(), status=exc.status_code)

        lock_dict = {'lock-free': '存储桶解锁:正常读写', 'lock-write': '存储桶加锁：锁定写',
                     'lock-readwrite': '存储桶加锁：锁定读写'}
        try:
            user_operation_record.add_log(request=request,
                                          operation_content=f'{lock_dict[lock]}, 桶名称:{bucket_name}',
                                          real_user=bucket.user)
        except Exception as e:
            pass

        if lock == 'lock-free':
            bucket_lock = Bucket.LOCK_READWRITE
        elif lock == 'lock-write':
            bucket_lock = Bucket.LOCK_READONLY
        elif lock == 'lock-readwrite':
            bucket_lock = Bucket.LOCK_NO_READWRITE
        else:
            exc = exceptions.InvalidArgument(message=_('参数“lock”的值无效。'), code='InvalidLock')
            return Response(data=exc.err_data(), status=exc.status_code)

        if not request.user.is_superuser:
            # 资源管理员
            try:
                self.check_resource_admin(request=request)
            except exceptions.NotFound as exc:
                return Response(data=exc.err_data(), status=exc.status_code)

        try:
            bucket.set_lock(bucket_lock)
        except exceptions.Error as exc:
            return Response(data=exc.err_data(), status=exc.status_code)

        return Response(data={}, status=200)

    @swagger_auto_schema(
        operation_summary=gettext_lazy('超级管理员/资源管理员给存储桶设置上限或过期时间'),
        request_body=no_body,
        manual_parameters=[
            openapi.Parameter(
                name='bucket_name',
                in_=openapi.IN_PATH,
                type=openapi.TYPE_STRING,
                required=True,
                description='存储桶名称'
            ),
            openapi.Parameter(
                name='upper_limit',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=False,
                description='上限(字节)'
            ),
            openapi.Parameter(
                name='expiration_timestamp',
                in_=openapi.IN_QUERY,
                type=openapi.TYPE_INTEGER,
                required=False,
                description='过期时间戳'
            ),
        ],
        responses={
            200: ""
        }
    )
    @action(methods=['post'], detail=True, url_path=r'limit', url_name='bucket-upl-limit')
    def bucket_set_upl_and_expt(self, request, *args, **kwargs):
        """
        超级管理员/资源管理员给存储桶加设置上限和过期时间

            http code 200 ok:
            {}

            http code 400, 403, 404, 500 error:
            {
              "code": "",
              "message": ""
            }

            * code:
            400:
                BadRequest:
            403：
                AccessDenied： 您没有执行该操作的权限。
            404：
                NoSuchBucket：存储桶不存在

        """
        bucket_name = kwargs.get(self.lookup_field)
        upper_limit = request.query_params.get('upper_limit', 0)
        if not upper_limit or upper_limit == '0':
            upper_limit = 0

        try:
            expiration_timestamp = float(request.query_params.get('expiration_timestamp', 0))
            upper_limit = int(upper_limit)
        except Exception as e:
            exc = exceptions.BadRequest(message=f'请填写正确的参数, 类型为int')
            return Response(data=exc.err_data(), status=exc.status_code)

        bucket = Bucket.get_bucket_by_name(bucket_name)
        if not bucket:
            exc = exceptions.NoSuchBucket(message=_('存储桶不存在。'))
            return Response(data=exc.err_data(), status=exc.status_code)

        time_ = time.time()
        #
        if expiration_timestamp == 0:
            bucket.expiration_time = None

        if expiration_timestamp != 0 and expiration_timestamp <= time_:
            exc = exceptions.BadRequest(message=_('时间戳不能小于当前。'))
            return Response(data=exc.err_data(), status=exc.status_code)

        if expiration_timestamp != 0 and expiration_timestamp > time_:
            timestamp = datetime.fromtimestamp(float(expiration_timestamp), tz=zoneinfo.ZoneInfo(settings.TIME_ZONE))
            bucket.expiration_time = timestamp

        if upper_limit >= 0:   #and bucket.upper_limit < upper_limit:  # 可以调大也可以条小
            bucket.upper_limit = upper_limit

        try:
            user_operation_record.add_log(request=request,
                                          operation_content=f'设置存储上限或过期时间(upper_limit={upper_limit}, expiration_time={expiration_timestamp}), 桶名称:{bucket_name}',
                                          real_user=bucket.user)
        except Exception as e:
            pass

        if not request.user.is_superuser:
            # 资源管理员
            try:
                self.check_resource_admin(request=request)
            except exceptions.NotFound as exc:
                return Response(data=exc.err_data(), status=exc.status_code)

        try:
            bucket.save()
        except exceptions.Error as exc:
            return Response(data=exc.err_data(), status=exc.status_code)

        return Response(data={}, status=200)


    def get_serializer_class(self):
        if self.action == 'create':
            return serializers.AdminBucketCreateSerializer
        elif self.action == 'list':
            return serializers.BucketNoPasswordSerializer

        return Serializer

    def get_permissions(self):

        return super().get_permissions()

    @staticmethod
    def check_resource_admin(request):
        """检查资源管理员"""
        # 资源管理员
        resource_user = GlobalConfig.objects.filter(name=GlobalConfig.ConfigName.RESOURCE_ADMINISTRATOR.value).first()

        if not resource_user:
            exc = exceptions.NotFound(message=_('请配置资源管理员信息。'))
            raise exc

        resource_user_list = GlobalConfig.parse_lines(text=resource_user.content)

        if request.user.username not in resource_user_list:
            exc = exceptions.NotFound(message=_('未找到资源管理员信息。'))
            raise exc

        return True

    def delete_nextcloud(self, bucket):
        """ 尝试删除 nextcloud 信息"""
        try:
            NextcloudHandler().delete_bucket_nextcloud(bucket=bucket)
        except NextcloudDBUserError as exc:
            logger.error('删除存储桶时,同时删除nextcloud 中信息' + str(exc.err_data_old()))
        except exceptions.BadRequest as exc:
            logger.error('删除存储桶时,同时删除nextcloud 中信息' + str(exc.err_data_old()))

        except exceptions.Error as exc:
            logger.error('删除存储桶时,同时删除nextcloud 中信息' + str(exc.err_data_old()))

        except Exception as e:
            logger.error('删除存储桶时,同时删除nextcloud 中信息' + str(e))
